package examples.parsing

import scala.util.parsing.combinator.syntactical.StandardTokenParsers
import scala.util.parsing.combinator.{JavaTokenParsers, RegexParsers}

object MyRegexParsers extends RegexParsers {
  val number = "[0-9]+".r

  // 表达式
  def expr: Parser[Any] = term ~ ("+" ~ term | "-" ~ term).*

  //
  def term = factor ~ rep("*" ~ factor)

  // 因子
  def factor: Parser[Any] = "(" ~ expr ~ ")" | number

  def main(args: Array[String]) {
    val input = "1+(4-2*4)*2+3"
//    val res = MyRegexParsers.parseAll(MyRegexParsers.expr, input)
//    println(res)
    val parser = new ArithParser2()
    val res = parser.parse(parser.expr(), input)
    println(res)

  }


}

trait ArithParser0 extends JavaTokenParsers {
  val a = term ~ rep("+" ~ term | "-" ~ term) ^^ {
    case number ~ list =>
      (number /: list) {
        case (acc, "+" ~ nextNum) => acc + nextNum
        case (acc, "-" ~ nextNum) => acc - nextNum
      }
  }
  def chainl[T, U](first: => Parser[T], p: => Parser[U], q: => Parser[(T, U) => T]): Parser[T]
  = first ~ rep(q ~ p) ^^ {
    case x ~ xs => xs.foldLeft(x: T){case (a, f ~ b) => f(a, b)} // x's type annotation is needed to deal with changed type inference due to SI-5189
  }

  val b = "+" ^^^ { (a: Double, b: Double) => a + b } | "-" ^^^ { (a: Double, b: Double) => a - b }
  def expr: Parser[Double] = chainl1(term, b)

  def term = chainl1(factor, "*" ^^^ { (a: Double, b: Double) => a * b } | "/" ^^^ { (a: Double, b: Double) => a / b })
  def factor = floatingPointNumber ^^ {_.toDouble} | "(" ~> expr <~ ")"
}
trait ArithParser extends JavaTokenParsers {
  def expr: Parser[Double] = chainl1(term, "+" ^^^ { (a: Double, b: Double) => a + b } | "-" ^^^ { (a: Double, b: Double) => a - b })
  def term = chainl1(factor, "*" ^^^ { (a: Double, b: Double) => a * b } | "/" ^^^ { (a: Double, b: Double) => a / b })
  def factor = floatingPointNumber ^^ {_.toDouble} | "(" ~> expr <~ ")"
}
class ArithParser2 extends JavaTokenParsers {
//  val Add: (Double, Double) => Double = (a: Double, b: Double) => a + b
  val Add: (Double, Double) => Double = (_: Double) + (_: Double)
  val Sub : (Double, Double) => Double= (a: Double, b: Double) => a - b
  val Mul: (Double, Double) => Double = (a: Double, b: Double) => a * b
  val Div: (Double, Double) => Double = (a: Double, b: Double) => a / b
  val Number: String => Double = (a: String) => a.toDouble

  def expr(): Parser[Double] = term * ( "+" ^^^ ((_: Double) + (_:Double)) | "-" ^^^ Sub)
  def term(): Parser[Double] = factor * ("*" ^^^ Mul | "/" ^^^ Div)
  def factor(): Parser[Double] = floatingPointNumber ^^ Number | "(" ~> expr() <~ ")"
}

object arithmeticParsers0 extends StandardTokenParsers {
  lexical.delimiters ++= List("(", ")", "+", "-", "*", "/")

  // 表达式
  def expr: Parser[Any] = term ~ rep("+" ~ term | "-" ~ term)

  //
  def term = factor ~ rep("*" ~ factor | "/" ~ factor)

  // 因子
  def factor: Parser[Any] = "(" ~ expr ~ ")" | numericLit

  def main(args: Array[String]) {
    val input = "1+(4-2*4)/2+(3)"
    val tokens = new lexical.Scanner(input)
    println(input)
    val res = phrase(expr)(tokens)
    println(res)
  }
}

object arithmeticParsers1 extends StandardTokenParsers {
  lexical.delimiters ++= List("(", ")", "+", "-", "*", "/")

  val reduceList: Int ~ List[String ~ Int] => Int = {
    case i ~ ps => (i /: ps) (reduce)
  }

  def reduce(x: Int, r: String ~ Int) = (r: @unchecked) match {
    case "+" ~ y => x + y
    case "-" ~ y => x - y
    case "*" ~ y => x * y
    case "/" ~ y => x / y
  }

  def expr: Parser[Int] = (term ~ rep("+" ~ term | "-" ~ term)).map(reduceList)

  def term: Parser[Int] = factor ~ rep("*" ~ factor | "/" ~ factor) ^^ reduceList

  def factor: Parser[Int] = "(" ~> expr <~ ")" | numericLit ^^ (_.toInt)

  def main(args: Array[String]) {
    val input = "1+(4-2*4)/2+(3)"
    val tokens = new lexical.Scanner(input)
    println(input)
    println(phrase(expr)(tokens))

  }
}


object arithmeticParsers2 extends StandardTokenParsers {

  class Expr

  case class BinOp(op: String, l: Expr, r: Expr) extends Expr

  case class Num(n: Int) extends Expr

  lexical.delimiters ++= List("(", ")", "+", "-", "*", "/")

  val reduceList: Expr ~ List[String ~ Expr] => Expr = {
    case i ~ ps => (i /: ps) (reduce)
  }

  def reduce(l: Expr, r: String ~ Expr) = BinOp(r._1, l, r._2)

  def mkNum(s: String) = Num(s.toInt)

  def expr: Parser[Expr] = term ~ rep("+" ~ term | "-" ~ term) ^^ reduceList

  def term: Parser[Expr] = factor ~ rep("*" ~ factor | "/" ~ factor) ^^ reduceList

  def factor: Parser[Expr] = "(" ~> expr <~ ")" | numericLit ^^ ((s: String) => Num(s.toInt))

  def main(args: Array[String]) {
    val input = "1+(4-2*4)/2+(3)"
    val parse = phrase(expr)
    val tokens = new lexical.Scanner(input)
    println(input)
    println(parse(tokens))
  }
}

